package cc.verywell.pureblock.msg.nfs.idx.extraDownload;

import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.DigestException;
import java.util.HashSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;

import cc.verywell.pureblock.func.node.KeyShell;
import cc.verywell.pureblock.msg.nfs.idx.extraDownload.DloTargetCheck.TargetCheck;

/**
* 
* 这是一个文件写入类，当文件在写入时，会锁定某些区域，但同时，其它区域是可以读取的
* 
* Copyright © 2020 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2020年2月3日 下午12:41:50
*/
public class DloTarget implements Closeable {

	private FileChannel channel;
	final public boolean isEmptyPath;//true:这是一个空转类
	public DloTarget(String absolutePath) throws FileNotFoundException {
		if(absolutePath!=null) {
			try {
				Files.createDirectories(Paths.get(absolutePath).getParent());
			} catch (IOException e) {
				throw new FileNotFoundException(e.getMessage());
			}
			RandomAccessFile fileaccess = new RandomAccessFile(absolutePath, "rw");
			this.channel = fileaccess.getChannel();
			isEmptyPath = false;
		}else{
			isEmptyPath = true;
		}
	}

	@Override
	synchronized public void close() throws IOException {
		if(check!=null)check.stop();
		FileChannel channel = this.channel;
		this.channel = null;
		if(channel!=null) {
			try {
				channel.force(true);
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				channel.close();
			}
		}
	}
	
	/**
	 * 会block直到可以锁定文件为止;
	 * @param newsize
	 * @throws IOException
	 */
	synchronized public void resetSize(long newsize) throws IOException {
		if(isEmptyPath)return; //这是一个空转类
		try(
				FileLock filelock = channel.lock();
		){
			if(newsize<channel.size())
				channel.truncate(newsize); ///这个参数只剪短，不加长
			else if(newsize>channel.size()) {
				channel.write(ByteBuffer.allocate(1), newsize-1); ///这个参数才是加长的，
			}
		}
	}
	
	private DloTargetCheck check = null;
	public DloTargetCheck useCheck(DloMap map) {
		if(check==null)check = new DloTargetCheck(isEmptyPath,channel,map);
		return check;
	}
	public boolean isUseCheck() {return check!=null;}
	
	
	//对外接口组
	public void joindata(ByteBuffer outbf, DltoolIterator<DltoolPosLen> pos, KeyShell key, byte[] hash,boolean isDataStock) throws InterruptedException,IOException, DigestException {
		//将指定数据写入指定位置;
		HashSet<DltoolPosLen> set = new HashSet<DltoolPosLen>(1); //隔离多余数据
		while(pos!=null) {
			if(set.add(pos.t)) {
				if(!isEmptyPath) {
					try(
							FileLock filelock = channel.lock(pos.t.pos,pos.t.len,false);
					){
						int position = outbf.position();
						int writen = channel.write(outbf, pos.t.pos);
						outbf.position(position);
						if(writen!=pos.t.len) throw new IOException("err write at "+pos.t.pos+":"+pos.t.len+" != "+writen);
					}
				}
				if(check!=null)check.join(new TargetCheck(key, hash, pos.t, isDataStock)); //有map在100%会启用
			}
			pos = pos.prv;
		}
	}
	

	
	
	
	
	
	
	


	
	
	
	
	
	

}
